package com.fishingwithme.infrastructure.math;

import com.fishingwithme.domain.common.Location;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 地图算法服务类（Spring Bean）
 * 包含多边形处理、点检测、交叉判断等核心功能
 */
@Service
public class MapAlgorithmService {

    /**
     * 计算多边形内中心点
     *
     * @param polygon 多边形顶点集合
     * @return 中心点坐标（确保在多边形内部），如果无效则返回 null
     */
    public Location calculateInnerCenter(List<Location> polygon) {
        if (polygon == null || polygon.size() < 3) {
            return null;
        }

        // 计算边界框（最小最大经纬度）
        double minLng = polygon.get(0).getLongitude();
        double maxLng = polygon.get(0).getLongitude();
        double minLat = polygon.get(0).getLatitude();
        double maxLat = polygon.get(0).getLatitude();

        for (int i = 1; i < polygon.size(); i++) {
            Location point = polygon.get(i);
            minLng = Math.min(minLng, point.getLongitude());
            maxLng = Math.max(maxLng, point.getLongitude());
            minLat = Math.min(minLat, point.getLatitude());
            maxLat = Math.max(maxLat, point.getLatitude());
        }

        // 边界框中心点
        Location boundingBoxCenter = new Location((minLng + maxLng) / 2, (minLat + maxLat) / 2);

        // 检查是否在多边形内部
        if (isLocationInPolygon(boundingBoxCenter, polygon)) {
            return boundingBoxCenter;
        }

        // 否则返回平均中心点
        double sumLng = 0;
        double sumLat = 0;
        for (Location p : polygon) {
            sumLng += p.getLongitude();
            sumLat += p.getLatitude();
        }

        return new Location(sumLng / polygon.size(), sumLat / polygon.size());
    }

    /**
     * 判断点是否在多边形内部（射线法）
     */
    public boolean isLocationInPolygon(Location point, List<Location> polygon) {
        if (point == null || polygon == null || polygon.isEmpty()) {
            return false;
        }

        int numVertices = polygon.size();
        boolean isInside = false;

        for (int i = 0, j = numVertices - 1; i < numVertices; j = i++) {
            Location vi = polygon.get(i);
            Location vj = polygon.get(j);

            if (isLocationEqual(point, vi)) return true;
            if (isLocationOnSegment(point, vi, vj)) return true;

            boolean isViLatBelowLocation = vi.getLatitude() < point.getLatitude();
            boolean isVjLatBelowLocation = vj.getLatitude() < point.getLatitude();

            if (isViLatBelowLocation != isVjLatBelowLocation) {
                double intersectLon = (
                        (point.getLatitude() - vi.getLatitude()) * (vj.getLongitude() - vi.getLongitude()) /
                                (vj.getLatitude() - vi.getLatitude())
                ) + vi.getLongitude();

                if (point.getLongitude() < intersectLon) {
                    isInside = !isInside;
                }
            }
        }

        return isInside;
    }

    /**
     * 检测当前线段集合是否存在交叉（包含首尾线段）
     */
    public boolean hasCrossingSegments(List<Location> points) {
        if (points == null || points.size() < 4) {
            return false;
        }

        for (int i = 0; i < points.size() - 1; i++) {
            for (int j = i + 2; j < points.size() - 1; j++) {
                Location seg1Start = points.get(i);
                Location seg1End = points.get(i + 1);
                Location seg2Start = points.get(j);
                Location seg2End = points.get(j + 1);

                if (doSegmentsIntersect(seg1Start, seg1End, seg2Start, seg2End)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * 检测闭合线段（最后一点到第一点）是否与其他线段交叉
     */
    public boolean hasClosingCrossing(List<Location> points) {
        if (points == null || points.size() < 3) return false;

        Location lastLocation = points.get(points.size() - 1);
        Location firstLocation = points.get(0);

        for (int i = 0; i < points.size() - 2; i++) {
            if (i == points.size() - 2 || i == 0) continue;

            Location segStart = points.get(i);
            Location segEnd = points.get(i + 1);

            if (doSegmentsIntersect(lastLocation, firstLocation, segStart, segEnd)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 预判即将形成的多边形是否与已有多边形重叠
     */
    public boolean willOverlapWithExisting(List<Location> currentLocations, List<List<Location>> existingPolygons) {
        if (currentLocations == null || currentLocations.size() < 3) return false;

        if (hasClosingCrossing(currentLocations)) {
            return true;
        }

        List<Location> tempPolygon = new java.util.ArrayList<>(currentLocations);
        tempPolygon.add(currentLocations.get(0));

        for (List<Location> polygon : existingPolygons) {
            if (isPolygonsOverlapping(tempPolygon, polygon)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 判断两个多边形是否重叠
     */
    public boolean isPolygonsOverlapping(List<Location> polygon1, List<Location> polygon2) {
        if (checkPolygonEdgesIntersect(polygon1, polygon2)) {
            return true;
        }

        if (isPolygonContained(polygon1, polygon2) || isPolygonContained(polygon2, polygon1)) {
            return true;
        }

        return false;
    }

    /**
     * 检查多边形边是否相交
     */
    public boolean checkPolygonEdgesIntersect(List<Location> polygon1, List<Location> polygon2) {
        if (polygon1 == null || polygon2 == null) return false;

        for (int i = 0; i < polygon1.size(); i++) {
            int j = (i + 1) % polygon1.size();
            for (int k = 0; k < polygon2.size(); k++) {
                int l = (k + 1) % polygon2.size();
                if (doSegmentsIntersect(polygon1.get(i), polygon1.get(j), polygon2.get(k), polygon2.get(l))) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 检查一个多边形是否被另一个多边形完全包含
     */
    public boolean isPolygonContained(List<Location> container, List<Location> contained) {
        if (container == null || contained == null) return false;

        for (Location p : contained) {
            if (!isLocationInPolygon(p, container)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 检查两个点是否相等
     */
    public boolean isLocationEqual(Location p1, Location p2) {
        if (p1 == null || p2 == null) return false;
        final double EPSILON = 1e-9;
        return Math.abs(p1.getLatitude() - p2.getLatitude()) < EPSILON &&
                Math.abs(p1.getLongitude() - p2.getLongitude()) < EPSILON;
    }

    /**
     * 检查点是否在线段上
     */
    public boolean isLocationOnSegment(Location point, Location start, Location end) {
        if (point == null || start == null || end == null) return false;

        double minLon = Math.min(start.getLongitude(), end.getLongitude());
        double maxLon = Math.max(start.getLongitude(), end.getLongitude());
        double minLat = Math.min(start.getLatitude(), end.getLatitude());
        double maxLat = Math.max(start.getLatitude(), end.getLatitude());

        if (point.getLongitude() < minLon || point.getLongitude() > maxLon ||
                point.getLatitude() < minLat || point.getLatitude() > maxLat) {
            return false;
        }

        double crossProduct = (end.getLongitude() - start.getLongitude()) * (point.getLatitude() - start.getLatitude()) -
                (end.getLatitude() - start.getLatitude()) * (point.getLongitude() - start.getLongitude());

        return Math.abs(crossProduct) < 1e-9;
    }

    /**
     * 检查两条线段是否相交
     */
    public boolean doSegmentsIntersect(Location p1, Location p2, Location q1, Location q2) {
        if (p1 == null || p2 == null || q1 == null || q2 == null) return false;

        int o1 = getOrientation(p1, p2, q1);
        int o2 = getOrientation(p1, p2, q2);
        int o3 = getOrientation(q1, q2, p1);
        int o4 = getOrientation(q1, q2, p2);

        if (o1 != o2 && o3 != o4) {
            return true;
        }

        if (o1 == 0 && isLocationOnSegment(q1, p1, p2)) return true;
        if (o2 == 0 && isLocationOnSegment(q2, p1, p2)) return true;
        if (o3 == 0 && isLocationOnSegment(p1, q1, q2)) return true;
        if (o4 == 0 && isLocationOnSegment(p2, q1, q2)) return true;

        return false;
    }

    /**
     * 计算三个点的方向
     *
     * @return 0: 共线, 1: 顺时针, 2: 逆时针
     */
    private int getOrientation(Location p, Location q, Location r) {
        double val = (q.getLongitude() - p.getLongitude()) * (r.getLatitude() - q.getLatitude()) -
                (q.getLatitude() - p.getLatitude()) * (r.getLongitude() - q.getLongitude());

        if (Math.abs(val) < 1e-9) return 0;
        return val > 0 ? 1 : 2;
    }

}
